56. 绘制条形图

本章概要

  • 学习材料:年度财务指标、收入构成、资产配置与杜邦三因子示例数据。
  • 本章任务:运行 fig-grouped-bar-outputfig-stacked-bar-outputfig-dupont-factors,实际绘制分组/堆叠图。
  • 完成后你将得到:三张条形图、分组数值表、ROE乘积复算和量纲说明。
  • 自我检查:检查堆叠合计与100%守恒、ROE三因子乘积一致;量纲混排时先检查原因。
  • 拓展练习:把杜邦分组条形图拓展应用到另一中性公司。

条形图:分类数据可视化的核心工具

条形图(Bar Plot)是展示分类数据最直观的工具。在金融分析中:

  • 比较数值:不同类别间的量值对比
  • 排名展示:Top N 股票、行业排名
  • 时间序列对比:同一指标不同时期的变化
  • 构成分析:各部分占总体的比例

条形图 vs 直方图:关键区别

特性 条形图 直方图
数据类型 分类变量 数值变量
X轴 离散类别 连续区间
条形宽度 可调整,有间隔 固定,无间隔
排列顺序 任意(通常按值排序) 固定(按区间边界)
应用场景 比较、排名 分布形态

运行前预测|平台任务1:创建股指月度涨跌幅数据

  • 输入预测:运行前先写出 datenameclosepriceindex_data 的业务含义、数据类型或取值范围,并判断哪一个输入最可能改变结果。
  • 结果预测:不展开答案,先预测将得到index_return 的结果;同时写出方向、数量级或表格/图形结构。
  • 完成要求:能独立说明本任务从输入到“平台任务1:创建股指月度涨跌幅数据”结果的关键步骤,原样录入平台代码并得到可核对的运行结果。

⭐ 平台任务1:创建股指月度涨跌幅数据

展开完整代码(投影默认折叠)
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import pandas as pd  # 导入Pandas数据分析库
import numpy as np  # 导入NumPy数值计算库

date=["2019年4月","2019年5月","2019年6月","2019年7月","2019年8月"]#创建一个日期的列表

name=['标普500指数','富时100指数','恒生指数','上证综指']#创建一个指数名称的列表

# 创建NumPy数组closeprice
closeprice=np.array([[2945.83,7418.22,29699.11,3078.34],[2752.06,7161.71,26901.09,

2898.70],[2941.76,7425.63,28542.62,2978.88],[2980.38,7586.78,27777.75,2932.51],  # 续行:closeprice=np.array(的数据项

[2926.46,7207.18,25724.73,2886.24]]) #创建收盘价的数据框

index_data = pd.DataFrame(data=closeprice,index=date,columns=name) #生成月末收盘价的数据框

index_return = index_data/index_data.shift(1)-1  #生成月度涨跌幅的数据框

index_return = index_return.dropna() #删除缺失值

print(index_return)  # 输出收益率数据

任务复盘|平台任务1:创建股指月度涨跌幅数据

运行后核对:核对 datenameclosepriceindex_data 是否按预测参与运算,实际输出是否与预测一致;若不一致,先检查类型、单位、索引/字段和运算顺序。

拓展练习:把数据替换为一个中国企业或市场序列,改变一个分组或时间窗口;说明图形结论是否改变,以及为何。

运行前预测|平台任务2:绘制四宫格条形图

  • 输入预测:运行前先写出 datenameclosepriceindex_data 的业务含义、数据类型或取值范围,并判断哪一个输入最可能改变结果。
  • 结果预测:不展开答案,先预测将得到index_return 的结果;同时写出方向、数量级或表格/图形结构。
  • 完成要求:能独立说明本任务从输入到“平台任务2:绘制四宫格条形图”结果的关键步骤,原样录入平台代码并得到可核对的运行结果。

⭐ 平台任务2:绘制四宫格条形图

展开完整代码(投影默认折叠)
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import pandas as pd  # 导入Pandas数据分析库
import numpy as np  # 导入NumPy数值计算库
from pylab import mpl  # 导入mpl模块
import matplotlib.pyplot as plt  # 导入Matplotlib绘图库
plt.rcParams['font.sans-serif']=['SimHei']  # 用黑体显示中文
plt.rcParams['axes.unicode_minus']=False   # 正常显示负号

date=["2019年4月","2019年5月","2019年6月","2019年7月","2019年8月"]#创建一个日期的列表
name=['标普500指数','富时100指数','恒生指数','上证综指']#创建一个指数名称的列表
# 创建NumPy数组closeprice
closeprice=np.array([[2945.83,7418.22,29699.11,3078.34],[2752.06,7161.71,26901.09,
2898.70],[2941.76,7425.63,28542.62,2978.88],[2980.38,7586.78,27777.75,2932.51],  # 续行:closeprice=np.array(的数据项
[2926.46,7207.18,25724.73,2886.24]]) #创建收盘价的数据框
index_data = pd.DataFrame(data=closeprice,index=date,columns=name) #生成月末收盘价的数据框
index_return = index_data/index_data.shift(1)-1  #生成月度涨跌幅的数据框
index_return = index_return.dropna() #删除缺失值
plt.figure(figsize=(11,10))  # 创建图形画布
plt.subplot(2,2,1) #第1行、第1列子图
# 绘制柱状图
plt.bar(x=index_return.columns,height=index_return.iloc[0],width=0.5,label=u"2019年5月涨跌幅",facecolor="y")
plt.xticks (fontsize=13)  # 设置X轴刻度标签
plt.yticks(fontsize=13)  # 设置Y轴刻度标签
plt.ylabel(u"涨跌幅",fontsize=13,rotation=90)  # 设置Y轴标签
plt.legend(loc=9,fontsize=13) #图列放置在中上位置
plt.grid(True)  # 显示网格线
plt.subplot(2,2,2,sharex=plt.subplot(2,2,1),sharey=plt.subplot(2,2,1)) #与第一个子图个的x轴和Y轴相同
# 绘制柱状图
plt.bar(x=index_return.columns,height=index_return.iloc[1],width=0.5,label=u"2019年6月涨跌幅",facecolor="c")
plt.xticks (fontsize=13)  # 设置X轴刻度标签
plt.yticks(fontsize=13)  # 设置Y轴刻度标签
plt.legend(loc=8,fontsize=13) #图列放置在中下位置
plt.ylabel(u"涨跌幅",fontsize=13,rotation=90)  # 设置Y轴标签
plt.grid(True)  # 显示网格线
plt.subplot(2,2,3,sharex=plt.subplot(2,2,1),sharey=plt.subplot(2,2,1))  # 选择子图位置
# 绘制柱状图
plt.bar(x=index_return.columns,height=index_return.iloc[2],width=0.5,label=u"2019年7月涨跌幅",facecolor="b")
plt.xticks (fontsize=13)  # 设置X轴刻度标签
plt.yticks(fontsize=13)  # 设置Y轴刻度标签
plt.legend(loc=9,fontsize=13)  # 添加图例
plt.ylabel(u"涨跌幅",fontsize=13,rotation=90)  # 设置Y轴标签
plt.grid(True)  # 显示网格线
plt.subplot(2,2,4,sharex=plt.subplot(2,2,1),sharey=plt.subplot(2,2,1))  # 选择子图位置
# 绘制柱状图
plt.bar(x=index_return.columns,height=index_return.iloc[3],width=0.5,label=u"2019年8月涨跌幅",facecolor="b")
plt.xticks (fontsize=13)  # 设置X轴刻度标签
plt.yticks(fontsize=13)  # 设置Y轴刻度标签
plt.legend(loc=9,fontsize=13)  # 添加图例
plt.ylabel(u"涨跌幅",fontsize=13,rotation=90)  # 设置Y轴标签
plt.grid(True)  # 显示网格线
plt.savefig("1.png")  # 保存图形至文件

任务复盘|平台任务2:绘制四宫格条形图

运行后核对:核对 datenameclosepriceindex_data 是否按预测参与运算,实际输出是否与预测一致;若不一致,先检查类型、单位、索引/字段和运算顺序。

拓展练习:把数据替换为一个中国企业或市场序列,改变一个分组或时间窗口;说明图形结论是否改变,以及为何。

运行前预测|平台任务3:水平条形图对比6月与7月涨跌幅

  • 输入预测:运行前先写出 datenameclosepriceindex_data 的业务含义、数据类型或取值范围,并判断哪一个输入最可能改变结果。
  • 结果预测:不展开答案,先预测将得到index_return 的结果;同时写出方向、数量级或表格/图形结构。
  • 完成要求:能独立说明本任务从输入到“平台任务3:水平条形图对比6月与7月涨跌幅”结果的关键步骤,原样录入平台代码并得到可核对的运行结果。

⭐ 平台任务3:水平条形图对比6月与7月涨跌幅

展开完整代码(投影默认折叠)
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import matplotlib.pyplot as plt  # 导入Matplotlib绘图库
import pandas as pd  # 导入Pandas数据分析库
import numpy as np  # 导入NumPy数值计算库
from pylab import mpl  # 导入mpl模块
 
plt.rcParams['font.sans-serif']=['SimHei']   # 用黑体显示中文
plt.rcParams['axes.unicode_minus']=False     # 正常显示负号

date=["2019年4月","2019年5月","2019年6月","2019年7月","2019年8月"]#创建一个日期的列表
name=['标普500指数','富时100指数','恒生指数','上证综指']#创建一个指数名称的列表
# 创建NumPy数组closeprice
closeprice=np.array([[2945.83,7418.22,29699.11,3078.34],[2752.06,7161.71,26901.09,
2898.70],[2941.76,7425.63,28542.62,2978.88],[2980.38,7586.78,27777.75,2932.51],  # 续行:closeprice=np.array(的数据项
[2926.46,7207.18,25724.73,2886.24]]) #创建收盘价的数据框
index_data = pd.DataFrame(data=closeprice,index=date,columns=name) #生成月末收盘价的数据框
index_return = index_data/index_data.shift(1)-1  #生成月度涨跌幅的数据框
index_return = index_return.dropna() #删除缺失值
plt.figure (figsize=(8,5))  # 创建图形画布
plt.barh(y=index_return.columns,width=index_return.iloc[1],height=0.5,label=u'2019年6月涨跌幅')  # 绘制柱状图
plt.barh(y=index_return.columns,width=index_return.iloc[2],height=0.5,label=u'2019年7月涨跌幅')  # 绘制柱状图
plt.xticks(fontsize=13)  # 设置X轴刻度标签
plt.xlabel(u"涨跌幅",fontsize=13)  # 设置X轴标签
plt.yticks (fontsize=13)  # 设置Y轴刻度标签
plt.title(u"比较4个股指在2019年6月和7月的月度涨跌幅",fontsize=13)  # 设置图表标题
plt.legend (loc=0,fontsize=13)  # 添加图例
plt.grid(True)  # 显示网格线
plt.show()  # 显示图形
plt.savefig("2.png")  # 保存图形至文件

任务复盘|平台任务3:水平条形图对比6月与7月涨跌幅

运行后核对:核对 datenameclosepriceindex_data 是否按预测参与运算,实际输出是否与预测一致;若不一致,先检查类型、单位、索引/字段和运算顺序。

拓展练习:把数据替换为一个中国企业或市场序列,改变一个分组或时间窗口;说明图形结论是否改变,以及为何。

关键参数解析:plt.bar()plt.barh()

  • x / y:类别标签(垂直用 x,水平用 y
  • height / width:条形的长度数据
  • width / height:条形的宽度(默认 0.8)
  • color / facecolor:条形填充颜色
  • label:图例标签,配合 plt.legend() 使用

水平条形图:适合类别名称较长的场景

展开完整代码
import matplotlib.pyplot as plt
import pandas as pd

plt.rcParams['font.sans-serif'] = ['Source Han Serif SC']
plt.rcParams['axes.unicode_minus'] = False

# 创建股票数据
stocks = ['贵州茅台', '五粮液', '招商银行', '中国平安', '平安银行',
          '工商银行', '建设银行', '农业银行', '中国银行', '交通银行']
returns = [0.05, 0.03, 0.02, 0.015, 0.018,
           0.012, 0.011, 0.01, 0.009, 0.008]

df_stocks = pd.DataFrame({
    '股票': stocks,
    '收益率': returns
}).sort_values('收益率')  # 按收益率升序排序

# 绘制水平条形图
plt.figure(figsize=(10, 4.2))
plt.barh(df_stocks['股票'], df_stocks['收益率'],
         color=['#E3120B' if x > 0.03 else '#008080' for x in df_stocks['收益率']],
         alpha=0.8)

# 添加数值标签
for i, (idx, row) in enumerate(df_stocks.iterrows()):
    plt.text(row['收益率'] + 0.002, i, f'{row["收益率"]:.1%}',
             va='center', fontsize=16)

plt.title('股票收益率排名', fontsize=16, fontweight='bold')
plt.xlabel('收益率', fontsize=16)
plt.ylabel('股票', fontsize=16)
plt.grid(axis='x', alpha=0.3)
plt.tight_layout()
plt.show()
图中展示水平条形图——股票收益率排名;读者应依据坐标、图例与注释比较主要模式。
Figure 1: 水平条形图——股票收益率排名

水平 vs 垂直条形图:如何选择?

场景 推荐类型 理由
类别名称较长 水平 避免标签重叠
类别数量多(>10) 水平 更易阅读
时间序列 垂直 符合时间从左到右的习惯
数值比较 垂直 长度差异更直观

颜色编码技巧:用不同颜色突出重要类别(如收益率 > 3% 用红色)

分组条形图:多指标同类别对比

Listing 1: 分组条形图的年度指标输入与位置
展开完整参考实现
import matplotlib.pyplot as plt
import numpy as np

plt.rcParams['font.sans-serif'] = ['Source Han Serif SC']
plt.rcParams['axes.unicode_minus'] = False

# 创建多年度数据
years = ['2021', '2022', '2023']
revenue = [100, 120, 135]      # 营业收入(亿元)
profit = [15, 18, 22]          # 净利润(亿元)
margin = [15, 15, 16.3]        # 净利率(%)

# 准备X轴位置和条形宽度
x = np.arange(len(years))
width = 0.25

# 数据准备与发图分离;下一单元自行创建完整 Figure
展开完整参考实现
fig, ax = plt.subplots(figsize=(10, 4.2))  # 在实际发图单元创建画布与坐标轴
bars1 = ax.bar(x - width, revenue, width, label='营业收入', color='#2E86AB', alpha=0.8)  # 绘制营业收入柱
bars2 = ax.bar(x, profit, width, label='净利润', color='#E3120B', alpha=0.8)  # 绘制净利润柱
bars3 = ax.bar(x + width, margin, width, label='净利率(%)', color='#008080', alpha=0.8)  # 绘制净利率柱
for bars in [bars1, bars2, bars3]:  # 逐指标处理数值标签
    for bar in bars:  # 逐柱读取高度
        height = bar.get_height()  # 取得当前柱形数值
        ax.text(bar.get_x() + bar.get_width()/2., height,
                f'{height:.1f}', ha='center', va='bottom', fontsize=16)  # 在柱顶标出数值
ax.set_xlabel('年份', fontsize=16)  # 标注年份轴
ax.set_ylabel('金额(亿元)/净利率(%)', fontsize=16)  # 显式标注混合量纲
ax.set_title('财务指标年度对比', fontsize=16, fontweight='bold')  # 标注图形主题
ax.set_xticks(x)  # 固定年度刻度位置
ax.set_xticklabels(years)  # 显示年度标签
ax.legend(fontsize=16)  # 显示指标图例
ax.grid(axis='y', alpha=0.3)  # 添加辅助网格
plt.tight_layout()  # 收紧图形布局
plt.show()  # 在当前单元发出完整图形
三个年度的营业收入、净利润和净利率分组条形对比。
Figure 2: 分组条形图——财务指标年度对比

分组条形图的核心技巧

条形位置计算公式:设 \(n\) 个年份,\(m\) 个指标,第 \(i\) 年第 \(j\) 个指标的位置:

\[ \large x_{ij} = i + (j - \frac{m+1}{2}) \times w \]

设计要点

  • 条形宽度:通常 0.2~0.3,太细不好看、太粗易重叠
  • 间距:组间间距 ≥ 组内间距
  • 颜色:同一组用同一色系,不同组用对比色

堆叠条形图:展示整体构成

Listing 2: 堆叠条形图的收入构成输入
展开完整参考实现
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['Source Han Serif SC']
plt.rcParams['axes.unicode_minus'] = False

# 创建收入构成数据
years = ['2021', '2022', '2023']
primary_business = [80, 90, 100]      # 主营业务收入(亿元)
other_business = [15, 20, 25]         # 其他业务收入(亿元)
investment_income = [5, 10, 10]       # 投资收益(亿元)

# 数据准备与发图分离;下一单元自行创建完整 Figure
展开完整参考实现
fig, ax = plt.subplots(figsize=(10, 4.2))  # 在实际发图单元创建画布与坐标轴
ax.bar(years, primary_business, label='主营业务收入', color='#2E86AB', alpha=0.8)  # 绘制第一层收入
ax.bar(years, other_business, bottom=primary_business, label='其他业务收入', color='#008080', alpha=0.8)  # 绘制第二层收入
stacked_bottom = [primary + other for primary, other in zip(primary_business, other_business)]  # 计算第三层柱形基线
ax.bar(years, investment_income, bottom=stacked_bottom, label='投资收益', color='#F0A700', alpha=0.8)  # 绘制第三层收入
for position, _year in enumerate(years):  # 逐年计算总收入标签
    total_value = primary_business[position] + other_business[position] + investment_income[position]  # 汇总年度收入构成
    ax.text(position, total_value + 5, f'{total_value}', ha='center', va='bottom', fontsize=16, fontweight='bold')  # 在柱顶标出总收入
ax.set_xlabel('年份', fontsize=16)  # 标注年份轴
ax.set_ylabel('收入(亿元)', fontsize=16)  # 标注收入量纲
ax.set_title('收入构成变化', fontsize=16, fontweight='bold')  # 标注图形主题
ax.legend(fontsize=16, loc='upper left')  # 显示收入构成图例
ax.grid(axis='y', alpha=0.3)  # 添加辅助网格
plt.tight_layout()  # 收紧图形布局
plt.show()  # 在当前单元发出完整图形
三个年度主营业务、其他业务和投资收益的堆叠条形对比。
Figure 3: 堆叠条形图——收入构成分析

堆叠条形图的应用场景与注意事项

适用场景

  • 构成分析:展示整体及各部分
  • 变化追踪:观察构成的时间变化
  • 对比分析:不同类别的构成差异

注意事项

  • 不适合比较各部分的大小(基线不统一)
  • 适合展示各部分占总体的比例
  • 考虑用百分比堆叠图使比例更清晰

百分比堆叠条形图:突出比例变化

Listing 3: 百分比堆叠条形图的资产配置输入
展开完整参考实现
import matplotlib.pyplot as plt
import numpy as np

plt.rcParams['font.sans-serif'] = ['Source Han Serif SC']
plt.rcParams['axes.unicode_minus'] = False

# 创建资产配置数据
years = ['2020', '2021', '2022', '2023', '2024']
stocks = [60, 65, 55, 50, 45]
bonds = [30, 25, 30, 35, 40]
cash = [10, 10, 15, 15, 15]

# 转换为百分比
total = np.array(stocks) + np.array(bonds) + np.array(cash)
stocks_pct = np.array(stocks) / total * 100
bonds_pct = np.array(bonds) / total * 100
cash_pct = np.array(cash) / total * 100

# 数据准备与发图分离;下一单元自行创建完整 Figure
展开完整参考实现
fig, ax = plt.subplots(figsize=(10, 4.2))  # 在实际发图单元创建画布与坐标轴
ax.bar(years, stocks_pct, label='股票', color='#E3120B', alpha=0.8)  # 绘制股票配置比例
ax.bar(years, bonds_pct, bottom=stocks_pct, label='债券', color='#008080', alpha=0.8)  # 绘制债券配置比例
ax.bar(years, cash_pct, bottom=stocks_pct + bonds_pct, label='现金', color='#F0A700', alpha=0.8)  # 绘制现金配置比例
for i, _year in enumerate(years):  # 逐年添加三类资产占比标签
    ax.text(i, stocks_pct[i]/2, f'{stocks_pct[i]:.0f}%',
            ha='center', va='center', color='white', fontweight='bold')  # 标注股票占比
    ax.text(i, stocks_pct[i] + bonds_pct[i]/2, f'{bonds_pct[i]:.0f}%',
            ha='center', va='center', color='white', fontweight='bold')  # 标注债券占比
    ax.text(i, stocks_pct[i] + bonds_pct[i] + cash_pct[i]/2, f'{cash_pct[i]:.0f}%',
            ha='center', va='center', color='white', fontweight='bold')  # 标注现金占比
ax.set_xlabel('年份', fontsize=16)  # 标注年份轴
ax.set_ylabel('配置比例(%)', fontsize=16)  # 标注比例量纲
ax.set_title('投资组合配置变化', fontsize=16, fontweight='bold')  # 标注图形主题
ax.set_ylim(0, 100)  # 固定百分比坐标范围
ax.legend(fontsize=16, loc='upper right', bbox_to_anchor=(1.15, 1))  # 显示资产类别图例
ax.grid(axis='y', alpha=0.3)  # 添加辅助网格
plt.tight_layout()  # 收紧图形布局
plt.show()  # 在当前单元发出完整图形
五个年度股票、债券和现金配置比例的百分比堆叠条形对比。
Figure 4: 百分比堆叠条形图——投资组合配置

金融应用:杜邦分析——ROE 分解

杜邦分析公式

\[ \large \text{ROE} = \frac{\text{净利润}}{\text{销售收入}} \times \frac{\text{销售收入}}{\text{总资产}} \times \frac{\text{总资产}}{\text{股东权益}} \]

三因子含义

  • 销售净利率:盈利能力,每1元销售带来的净利润
  • 总资产周转率:效率,每1元资产创造的销售收入
  • 权益乘数:杠杆,资产与权益的比率

杜邦分析可视化:ROE 对比

展开完整参考实现
import matplotlib.pyplot as plt
import numpy as np

plt.rcParams['font.sans-serif'] = ['Source Han Serif SC']
plt.rcParams['axes.unicode_minus'] = False

# 杜邦分析数据
companies = ['公司A', '公司B', '公司C', '公司D', '公司E']
net_margin = [15, 10, 8, 20, 12]
asset_turnover = [1.2, 1.5, 0.8, 1.0, 1.3]
equity_multiplier = [2.0, 1.8, 2.5, 1.5, 2.2]
roe = [nm * at * em / 100 for nm, at, em in
       zip(net_margin, asset_turnover, equity_multiplier)]

fig, ax = plt.subplots(figsize=(9, 4.5))

# ROE对比
ax.bar(companies, roe, color='#2E86AB', alpha=0.8)
ax.set_title('ROE对比', fontsize=18, fontweight='bold')
ax.set_ylabel('ROE (%)', fontsize=16)
ax.set_ylim(0, max(roe) * 1.2)
ax.grid(axis='y', alpha=0.3)
for i, v in enumerate(roe):
    ax.text(i, v + 0.01, f'{v:.1%}', ha='center', va='bottom', fontsize=16)
plt.tight_layout()
plt.show()
图中展示杜邦分析——ROE对比;读者应依据坐标、图例与注释比较主要模式。
Figure 5: 杜邦分析——ROE对比

杜邦分析可视化:三因子分解

展开三因子绘图代码
fig, ax = plt.subplots(figsize=(9, 4.5))
x = np.arange(len(companies))
width = 0.25
ax.bar(x - width, net_margin, width, label='销售净利率(%)',
           color='#E3120B', alpha=0.8)
ax.bar(x, [at * 10 for at in asset_turnover], width,
           label='总资产周转率(x10)', color='#008080', alpha=0.8)
ax.bar(x + width, [em * 5 for em in equity_multiplier], width,
           label='权益乘数(x5)', color='#F0A700', alpha=0.8)
ax.set_title('杜邦三因子', fontsize=18, fontweight='bold')
ax.set_xticks(x)
ax.set_xticklabels(companies, fontsize=16)
ax.legend(fontsize=16)
ax.grid(axis='y', alpha=0.3)

plt.tight_layout()
plt.show()
图中展示杜邦分析——三因子分解;读者应依据坐标、图例与注释比较主要模式。
Figure 6: 杜邦分析——三因子分解

杜邦分析的行业特点

高 ROE 可能来自不同驱动因素:

驱动因子 典型行业 特征
高利润率 白酒、医药 品牌溢价,毛利率高
高周转率 零售、快消 薄利多销,资产效率高
高杠杆 银行、地产 负债经营,放大收益

核心要点:需结合行业特点综合分析,不能仅看 ROE 数值

本章小结

  • plt.bar():绘制垂直条形图,适合时间序列和数值比较
  • plt.barh():绘制水平条形图,适合类别名称较长的排名展示
  • 分组条形图:通过调整 x 轴位置偏移实现多指标并列对比
  • 堆叠条形图:通过 bottom 参数实现各部分的层叠展示
  • 百分比堆叠图:先将数据转为百分比,再堆叠,突出比例变化

随堂练习

  • 问题 1|需要准备哪些数据?:年度财务指标、收入构成、资产配置与杜邦三因子示例数据。
  • 问题 2|需要完成哪些操作?:运行 fig-grouped-bar-outputfig-stacked-bar-outputfig-dupont-factors,实际绘制分组/堆叠图。
  • 问题 3|应得到哪些结果?:三张条形图、分组数值表、ROE乘积复算和量纲说明。
  • 问题 4|怎样确认结果可靠?:检查堆叠合计与100%守恒、ROE三因子乘积一致;量纲混排时先检查原因。
  • 问题 5|换一个情境,怎样继续应用?:把杜邦分组条形图拓展应用到另一中性公司。
  • 作答提示:请依次写清所用数据、分析过程、所得结果、核对方法和拓展思考。课程所需数据见前言中的下载入口;教学平台固定题按页面说明完成。

教师参考解答|答案与说明 1

  • 所用数据与字段:年度×业务线金额表;年度与类别唯一、金额非负且同一单位。

教师参考解答|代码 1

展开代码(代码区可独立滚动)
import numpy as np  # 导入数值工具以执行守恒核对
import pandas as pd  # 导入表格工具以组织年度构成
import matplotlib.pyplot as plt  # 导入绘图工具以生成三类条形图
amounts=pd.DataFrame({'核心业务':[60.,66.,72.],'增值业务':[25.,30.,36.],'其他业务':[15.,14.,12.]},index=['2023','2024','2025'])  # 构造年度×业务线同单位金额表
assert amounts.index.is_unique and amounts.columns.is_unique and (amounts>=0).all().all()  # 执行年度、类别唯一与非负输入要求
year_totals=amounts.sum(axis=1)  # 计算每年堆叠金额总量
shares=amounts.div(year_totals,axis=0)*100  # 把每年构成归一为百分比
fig,axes=plt.subplots(1,3,figsize=(13,3.8))  # 创建分组、堆叠与100%堆叠三面板
amounts.plot.bar(ax=axes[0],width=.75,title='分组条形图')  # 实际生成年度分组条形图
amounts.plot.bar(ax=axes[1],stacked=True,width=.75,title='堆叠条形图')  # 实际生成年度金额堆叠图
shares.plot.bar(ax=axes[2],stacked=True,width=.75,title='100%堆叠条形图')  # 实际生成年度百分比堆叠图
axes[0].set_ylabel('金额(万元)')  # 标注分组图金额单位
axes[1].set_ylabel('金额(万元)')  # 标注堆叠图金额单位
axes[2].set_ylabel('构成比例(%)')  # 标注百分比图单位
assert np.allclose(amounts.sum(axis=1),year_totals)  # 逐年检查金额堆叠守恒
assert np.allclose(shares.sum(axis=1),np.full(len(shares),100.0))  # 逐年检查100%堆叠守恒
assert all(len(axis.patches)==len(amounts)*len(amounts.columns) for axis in axes)  # 逐图检查全部年度与业务组均已绘制
plt.tight_layout()  # 收紧教师答案三图布局
plt.show()  # 显示三类条形图结果
print('逐年金额守恒',year_totals.to_dict())  # 输出逐年堆叠合计
print('逐年100%守恒',shares.sum(axis=1).round(10).to_dict())  # 输出逐年百分比守恒

教师参考解答|答案与说明 2

  • 拓展应用答案|杜邦分解教学示例(非公司观测)

教师参考解答|代码 2

展开代码(代码区可独立滚动)
dupont_fixture=pd.DataFrame({'category':['对象A','对象B'],'net_margin':[.08,.05],'asset_turnover':[1.2,1.5],'equity_multiplier':[1.8,2.2]})  # 构造不对应真实公司的杜邦教学示例
dupont_fixture['roe']=dupont_fixture['net_margin']*dupont_fixture['asset_turnover']*dupont_fixture['equity_multiplier']  # 按同一口径重构ROE
dupont_long=dupont_fixture.set_index('category')[['net_margin','asset_turnover','equity_multiplier']].rename(columns={'net_margin':'净利率','asset_turnover':'资产周转率','equity_multiplier':'权益乘数'})  # 转成长表友好的三因素矩阵
ax=dupont_long.plot.bar(figsize=(7,3.3),title='教学示例,非公司观测|杜邦三因素')  # 用分组条形图比较三个构成因素
ax.set_ylabel('示例数据数值(量纲见图例)'); plt.tight_layout(); plt.show()  # 明示不同量纲并保持可见边界
manual_roe=dupont_fixture[['net_margin','asset_turnover','equity_multiplier']].prod(axis=1)  # 独立逐行相乘复算ROE
assert np.allclose(dupont_fixture['roe'],manual_roe,atol=1e-12)  # 用容差核对杜邦乘积恒等式
print(dupont_fixture)  # 输出三因素与重构ROE供核对

教师参考解答|答案与说明 3

  • 解释答案:条形图比较离散类别,分组条回答并列年度差异,堆叠条回答组成;纵轴零点、单位和类别顺序会直接影响解释。
  • 拓展应用答案:上方代码把对象替换为中性杜邦示例数据,保持净利率×资产周转率×权益乘数的口径,输出三因素分组条与重构ROE,并用逐行乘积容差检查核对;不得把不同量纲的柱高直接相加。
  • 参考结果:同一输入生成的分组、堆叠和100%堆叠三图,逐年金额/百分比守恒输出,以及杜邦拓展应用图与ROE表。另行核对:逐年求和、逐年归一到100%、逐对象相乘并核对图中条数。
  • 常见错误:百分比未按年归一;分组与堆叠使用不同输入;杜邦因素混口径。